#pragma once
#include <mutex>
#include <condition_variable>
#include <vector>
template <typename T>
class BlockingQueue {
    std::mutex              _mutex;
    std::condition_variable _not_full;
    std::condition_variable _not_empty;
    int                     _start;
    int                     _end;
    int                     _capacity;
    std::vector<T>          _vt;

public:
    BlockingQueue(const BlockingQueue<T>& other) = delete;
    BlockingQueue<T>& operator=(const BlockingQueue<T>& other) = delete;
    BlockingQueue(int capacity) : _capacity(capacity), _vt(capacity + 1), _start(0), _end(0) {}

    bool isempty() {
        return _end == _start;
    }

    bool isfull() {
        return (_start + _capacity - _end) % (_capacity + 1) == 0;
    }

    void push(const T& e) {
        std::unique_lock<std::mutex> lock(_mutex);
        while (isfull()) {
            if (_not_full.wait_for(lock,std::chrono::milliseconds(300))==std::cv_status::timeout)
            {
                return;
            }
        }

        _vt[_end++] = e;
        _end %= (_capacity + 1);
        _not_empty.notify_one();
    }

    T pop() {
        std::unique_lock<std::mutex> lock(_mutex);
        while (isempty()) {
            if (_not_empty.wait_for(lock,std::chrono::milliseconds(300))==std::cv_status::timeout)
            {
                return nullptr;
            }
        }

        auto res = _vt[_start++];
        _start %= (_capacity + 1);
        _not_full.notify_one();
        return res;
    }
};
